from typing import Optional
from fastapi import APIRouter, Depends, Response, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
import config
from schemas.users import UserRoleForm, UserModel, UserRoleModel, UserRolePagination, \
    CreateUserRequest, UserForm, UserLoginResponse, UsersPagination, RefreshTokenRequest, \
    RefreshTokenResponse, ChangePasswordRequest, AdminChangePasswordRequest, ChangeUserInfoRequest
from db import get_db, crud, models
from sqlalchemy.orm import Session
from enums import UserRoleEnum
from datetime import datetime, timedelta
from loguru import logger
import jwt
from jwt import DecodeError
import utils
from utils.response import HTTPException

router = APIRouter()


# 用户角色
@router.post("/roles", response_model=UserRoleModel)
def create_user_role(data: UserRoleForm, db: Session = Depends(get_db)):
    try:
        return crud.users.UserRole.create(db, data)
    except Exception as err:
        raise HTTPException(message=str(err))


@router.get("/roles/{id}", response_model=UserRoleModel)
def get_user_role_by_id(id: int, db: Session = Depends(get_db)):
    try:
        return crud.users.UserRole.get(db, id)
    except Exception as err:
        raise HTTPException(message=str(err))


@router.put("/roles")
def update_user_role(data: UserRoleForm, db: Session = Depends(get_db)):
    try:
        crud.users.UserRole.update(db, data)
        return Response(status_code=200)
    except Exception as err:
        raise HTTPException(message=str(err))


@router.delete("/roles/{id}")
def delete_user_role(id: int, db: Session = Depends(get_db)):
    try:
        crud.users.UserRole.delete(db, id)
        return Response(status_code=200)
    except Exception as err:
        raise HTTPException(message=str(err))


@router.get("/roles", response_model=UserRolePagination)
def get_user_roles(page: int, per_page: int, db: Session = Depends(get_db)):
    try:
        total, data = crud.users.UserRole.gets(db, page - 1, per_page)
        return UserRolePagination(
            page=page,
            per_page=per_page,
            total=total,
            data=data
        )
    except Exception as err:
        raise HTTPException(message=str(err))


def get_token(user_id: int) -> dict:
    # access token
    access_expire = datetime.now() + timedelta(seconds=config.ACCESS_TOKEN_EXPIRE)
    access_claims = {"user_id": user_id, "iss": "server", "sub": "access token", "exp": access_expire}
    access_token = jwt.encode(access_claims, config.SECRET_KEY, algorithm=config.ALGORITHM)

    # refresh token
    refresh_expire = datetime.now() + timedelta(seconds=config.REFRESH_TOKEN_EXPIRE)
    refresh_claims = {"user_id": user_id, "iss": "server", "sub": "refresh token", "exp": refresh_expire}
    refresh_token = jwt.encode(refresh_claims, config.REFRESH_SECRET_KEY, algorithm=config.ALGORITHM)

    return {
        "access_token": access_token,
        "refresh_token": refresh_token
    }


@router.post("/login", response_model=UserLoginResponse)
def login(form: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    user = crud.users.Users.get_by_username(db, form.username)
    if user is None:
        raise HTTPException(message="用户不存在")

    if user.password == utils.encrypt.md5_pwd(form.password):
        token = get_token(user.id)
        expire = datetime.now() + timedelta(seconds=config.ACCESS_TOKEN_EXPIRE)

        return UserLoginResponse(
            id=user.id,
            nickname=user.nickname,
            username=user.username,
            avatar=user.avatar,
            last_login_time=user.last_login_time,
            permissions=user.permissions,
            roles=user.roles,
            access_token=token["access_token"],
            refresh_token=token["refresh_token"],
            expires=int(expire.timestamp() * 1000),
            token_type="bearer",
        )
    else:
        raise HTTPException(message="密码错误")


oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/users/login")


@router.post("/refresh-token", response_model=RefreshTokenResponse)
def handle_refresh_token(data: RefreshTokenRequest, db: Session = Depends(get_db)):
    try:
        data = jwt.decode(data.refresh_token, config.REFRESH_SECRET_KEY, algorithms=[config.ALGORITHM])
        user = crud.users.Users.get(db, data["user_id"])
        if user is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail={"msg": "用户不存在"}
            )

        token = get_token(user.id)
        expire = datetime.now() + timedelta(seconds=config.ACCESS_TOKEN_EXPIRE)

        return RefreshTokenResponse(
            access_token=token["access_token"],
            refresh_token=token["refresh_token"],
            expires=int(expire.timestamp() * 1000)
        )
    except DecodeError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail={"msg": "Token验证失败"}
        )


async def verify_token(token: str = Depends(oauth2_scheme), db: Session = Depends(get_db)) -> UserModel:
    try:
        data = jwt.decode(token, config.SECRET_KEY, algorithms=[config.ALGORITHM])
        user = crud.Users.get(db, data["user_id"])
        if user is None:
            raise HTTPException(
                status_code=status.HTTP_401_UNAUTHORIZED,
                detail={"msg": "用户不存在"}
            )
        return UserModel.from_orm(user)
    except DecodeError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail={"msg": "Token验证失败"}
        )


# 用户管理
@router.get("/pagination", dependencies=[Depends(verify_token)], response_model=UsersPagination)
def get_user_pagination(page: int, page_size: int, kw: Optional[str] = None, db: Session = Depends(get_db)):
    try:
        total, data = crud.users.Users.gets(db, page - 1, page_size, kw)
        return UsersPagination(
            page=page,
            page_size=page_size,
            total=total,
            data=data
        )
    except Exception as err:
        raise HTTPException(message=str(err))


@router.post("/register")
def register(data: CreateUserRequest, user: UserModel = Depends(verify_token), db: Session = Depends(get_db)):
    exist = crud.users.Users.get_by_username(db, data.username)
    if exist is not None:
        return HTTPException(message="账号重复")

    user = UserForm(
        nickname=data.nickname,
        username=data.username,
        password=utils.encrypt.md5_pwd(data.password),
        created_on=datetime.now(),
        created_by=user.id,
        roles=data.roles
    )

    if crud.users.Users.count(db) == 0:
        user.roles = [UserRoleEnum.ADMIN]

    crud.users.Users.create(db, user)
    return Response(status_code=200)


@router.put("/admin-change-password")
def admin_change_password(data: AdminChangePasswordRequest, admin: UserModel = Depends(verify_token),
                          db: Session = Depends(get_db)):
    user = db.query(models.Users).filter_by(id=data.id, status=1).first()
    if user is None:
        raise HTTPException(message="账号不存在")

    user.password = utils.encrypt.md5_pwd(data.password)
    user.updated_by = admin.id
    user.updated_on = datetime.now()

    db.commit()
    return Response(status_code=200)


@router.put("/change-password")
def change_password(data: ChangePasswordRequest, user: UserModel = Depends(verify_token),
                    db: Session = Depends(get_db)):
    u: models.Users = db.query(models.Users).get(user.id)

    if u is None:
        raise HTTPException(message="账号不存在")

    if utils.encrypt.md5_pwd(data.old_password) != u.password:
        raise HTTPException(message="密码错误")

    u.password = utils.encrypt.md5_pwd(data.password)
    u.updated_by = user.id
    u.updated_on = datetime.now()

    db.commit()
    return Response(status_code=200)


@router.put("/change-info")
def change_user_info(data: ChangeUserInfoRequest, user: UserModel = Depends(verify_token),
                     db: Session = Depends(get_db)):
    u: models.Users = db.query(models.Users).get(user.id)

    if u is None:
        raise HTTPException(message="账号不存在")

    u.nickname = data.nickname

    if data.avatar is not None:
        u.avatar = data.avatar

    u.updated_by = user.id
    u.updated_on = datetime.now()

    db.commit()
    return Response(status_code=200)


@router.delete("/{user_id}", dependencies=[Depends(verify_token)])
def delete_user(user_id: int, user: UserModel = Depends(verify_token), db: Session = Depends(get_db)):
    try:
        db.query(models.Users).filter_by(id=user_id).update({
            "status": 0,
            "updated_on": datetime.now(),
            "updated_by": user.id
        })
        db.commit()
        return Response(status_code=200)
    except Exception as err:
        raise HTTPException(message=str(err))
